GameKit
provides peer-to-peer connectivity between iPhone and iPod touch
devices. New to the 3.0 SDK, this framework helps you create
interconnected applications that exchange live data in real time.
In
its default implementation, GameKit works by creating and managing an
ad hoc Bluetooth network that lets devices find each other, establish a
connection, and transmit data through that connection. Starting with
the iPhone 3.1 firmware, GameKit also allows you to find, connect, and
transmit to devices on the same WiFi network. Using Bluetooth is a fast
and reliable approach to interdevice communications. Unfortunately,
Bluetooth communication using GameKit is not supported for first
generation iPhones and iPod touches. GameKit offers an online (WiFi- or
Internet-based) mode as well as the Bluetooth mode, but at the time of
writing this book it’s basically a “bring your own technology to the
table” option.
Although,
as the name suggests, you can build games with GameKit, you can do far
more. You can create applications to support collaborative layout,
picture sharing, chats, and more. So long as the same application
exists on both devices, you can establish GameKit communications either with (3.1 firmware or later) or without a shared WiFi network.
GameKit Bluetooth Limitations
All
you need is proximity. GameKit’s Bluetooth-based applications are
limited to about 10 meters, or 30 feet. So think of your audience
including people riding together on a train or in a car, in a
convention hall’s meeting room, or working in the same office. Within
that range, your application can easily establish a peer-to-peer
connection.
GameKit
offers excellent performance for short, tight blips of information.
Apple recommends that GameKit transmissions be limited to 1,000 bytes
and under. Although GameKit can handle larger blobs, up to 95 Kilobytes
at a time, it’s not meant for use as general device-to-device data
transfer. Try to send too much data at once and you will receive
transmission errors.
If
you must transfer large files, you need to break those files into
manageable chunks. Make sure you use standard handshaking and packet
checksumming techniques to ensure the reliability of your data.
Device Limitations
GameKit’s
Bluetooth networking is not for every iPhone and iPod touch. It works
with the 3G iPhone and later models and with the second generation iPod
touch and later models. You cannot deploy a GameKit Bluetooth
application to first generation iPhones and iPod touch units. Plus,
GameKit Bluetooth is only partially supported on the Simulator. That
is, you can discover nearby devices, but you cannot actually connect to
them.
As of the 3.1 SDK, Apple added support for the peer-peer key in the Info.plist UIRequiredDeviceCapabilities
entry. This key indicates that your application requires peer-to-peer
connectivity over Bluetooth. For firmware earlier than 3.1, you can
specify required device features like telephony and microphone, but there’s no key available to describe a Bluetooth networking prerequisite.
Make
it very clear in your marketing materials which devices you do and do
not support, especially if your application centers on nearby iPhone
connectivity. Users cannot use GameKit Bluetooth features on
noncompliant devices. When attempting to do so, they’ll receive a
message like the one shown in Figure 1, which displays a Bluetooth logo and says, “Not supported on this iPhone” (or iPod).
This
is why you should strongly consider offering an “online mode” fallback.
The same interface that moves you to the Bluetooth-powered “nearby
mode” provides a unified GUI allowing users to access other networking
options.
Sessions
GameKit’s
peer-to-peer connections are built using Bonjour networking. Bonjour,
which is Apple’s trade name for zero configuration networking, allows
devices to advertise and discover network services. Built into Mac OS X
since version 10.2, Bonjour offers these features without calling
attention to itself. For example, Bonjour powers the features that let
users find shared music for iTunes or connect to wireless printers
without requiring custom configuration. These services automatically
appear when they become available and disappear when they’re not. It’s
a powerful OS feature.
GameKit
provides that same Bonjour power without having to build the often
complicated Bonjour callbacks for registering and detecting services.
With GameKit, you request a connection using a “peer picker” controller
and then manage a “session” once the connection has been established.
GameKit’s
session objects provide a single focus point for data transfer
management. Each session uses a unique name, which you choose, to
advertise itself. When an application looks for another device to
connect to, it uses this name to identify compatible services.
If
you use a Bonjour browsing service to look for that name, you’ll fail.
Apple encodes the service name. For example, a service called
“MacBTClient Sample” becomes the “_11d7n7p5tob54j._udp.” Bonjour
service. GameKit automatically transforms the name you supply so it
knows how to find matching services.
Unfortunately,
there’s no Mac OS X or Windows API available to let you build services
from a desktop system that would let you hook into GameKit. Apple’s
name encryption more or less guarantees that standard Bonjour communications will not work with iPhone-based GameKit applications.
You read about bypassing this limitation later in this chapter by working directly with Bonjour.
Servers, Clients, Peers
GameKit
offers three session modes; applications can act as servers, clients,
and peers. Servers advertise a service and initialize a session,
allowing clients to search for and connect to them. This is the kind of
behavior that a smart printer uses, letting clients find and use its
capabilities. It’s handy for devices that provide a fixed functionality
but it’s not the best choice for most iPhone applications, especially
games.
Peers
work as both servers and clients. They advertise and search
simultaneously. Once a peer selects a service, its client/server role
is hidden both from the user and from the developer. This makes the
peer approach very easy to develop for iPhones. You don’t have to build
separate client and server applications. One peer-based application
does all the work.
The Peer Connection Process
The peer picking process is handled by a class called GKPeerPickerController.
It provides a built-in series of interactive alert dialogs that
automate the task of advertising device availability and selecting a
peer. Using this class is not mandatory. You can bypass it and create a
custom class to search for and connect to peers. For simple
connections, however, the GKPeerPickerController class offers a ready-to-use interface that sidesteps the need for detecting and negotiating with peers.
To use the peer picker, you allocate it, set a delegate (which must implement the GKPeerPickerControllerDelegate
protocol), and show it. For targets earlier than the 3.1 SDK, avoid
using autorelease with the picker. Instead, wait for a delegate
callback and release it there, ensuring that you will not run into
unexplained application crashes when the dismissal animation fails.
This issue was fixed in the 3.1 firmware. With 3.1 or later, you can
choose to release in the callback (3.0 compatible) or use autorelease
(3.1-or-later compatible).
As
mentioned, GameKit supports two kinds of connections: nearby (via
Bluetooth) and online (via the Internet and WiFi). The code in this
chapter’s first few recipes exclusively uses a nearby connection; an
online recipe appears toward the end of this chapter. The
Internet-style approach is less friendly and minimally documented at
the time of writing this book. In contrast, the nearby Bluetooth style
is friendly, easy-to-use, and ready for inclusion in your applications.
Displaying the Peer Picker
The
following code allocates and shows a new peer picker controller,
setting its connection style to Nearby. This skips an optional
interaction step (discussed later in this chapter) where a user selects between Online and Nearby modes. When presented, it shows the interface in Figure 2.
You do not have to use a peer picker to establish GameKit sessions. The
iPhone SDK now lets you create your own custom interfaces to work with
the underlying GameKit connections. A sample that demonstrates how to
do so has been added to the sample code that accompanies this chapter.
// Create and present a new peer picker
GKPeerPickerController *picker = [[[GKPeerPickerController alloc]
init];
picker.delegate = self;
picker.connectionTypesMask = GKPeerPickerConnectionTypeNearby;
[picker show];
When your mask includes the online type as well (GKPeerPickerConnectionTypeOnline), the picker first asks the user which kind of connection to use before moving on to either the nearby connection interface of Figure 12-2 or to a custom online interface that you must build yourself.
Pressing Cancel
Users may cancel out of the peer picker alert. When they do so, the delegate receives a peerPickerControllerDidCancel:
callback. If you display a “connect” button in your application, make
sure to restore it at this point so the user can try again.
Creating the Session Object
As
the picker delegate, you must supply a session object on request.
Sessions, which provide an abstract class that creates and manages a
data socket between devices, belong to the GKSession
class and must be initialized with a session identifier. This is the
unique string used to create the Bonjour service and link together two
iPhone devices (peers) both advertising the same service. By setting
the display name to nil, the session uses the built-in device name.
- (GKSession *)peerPickerController:(GKPeerPickerController *)picker
sessionForConnectionType:(GKPeerPickerConnectionType)type
{
// Create a new session if one does not already exist
if (!self.session) {
self.session = [[[GKSession alloc] initWithSessionID:
(self.sessionID ? self.sessionID : @"Sample Session")
displayName:nil sessionMode:GKSessionModePeer]
autorelease];
self.session.delegate = self;
}
return self.session;
}
Although
this is an optional method, you’ll usually want to implement it so you
can set your session ID and mode. Upon detecting another iPhone or iPod
with the same advertised service ID, the peer picker displays the peer
as a compatible match, as shown in Figure 3.
Waiting
for the peer picker list can take a few seconds or up to a few minutes.
During development, you usually need to allow your Bonjour network
stack to clear out any previous sessions when you iterate on the code.
That’s what typically causes the longer delays. Apple recommends always
debugging from a clean restart. If debugging delays get frustrating
enough, make sure to reboot.
In normal use, connection delays usually hover around 45 seconds at a maximum. Warn your users to be patient. In Figure 3,
Binky is the device name for a second iPhone running the same
application. When the user taps the name Binky, this iPhone
automatically goes into client mode, and Binky goes into server mode.
Client and Server Modes
When
a device changes into client mode, it stops advertising its service.
The Choose an iPhone or iPod Touch dialog shown previously in Figure 12-3
changes on the server unit. The client’s peer name dims to dark gray
and the words “is not available” appear underneath. A few seconds later
(and this can actually run up to a minute, so again warn your users
about delays), both units update their peer picker display.
Figure 4
shows the server and client peer pickers during this process. The
client waits as the server receives the connection request (left). On
the server, the host user must accept or decline the connection
(middle). Should they decline, an updated peer picker notifies the
client (right). If they accept, both delegates receive a new callback.
The
delegate callback lets the new peers dismiss the peer picker and to set
their data received handler. Make sure to release the picker at this
time.
- (void)peerPickerController:(GKPeerPickerController *)picker
didConnectPeer:(NSString *)peerID
toSession: (GKSession *) session
{
// Dismiss and release the picker, then set the data handler
[picker dismiss];
[picker release];
[self.session setDataReceiveHandler:self withContext:nil];
}